using System;
using System.Collections;
using System.Collections.Generic;

namespace EntityKit.Runtime
{
    /// <summary>
    /// 可观察栈类，当栈内容发生变化时会触发相应的事件
    /// </summary>
    /// <typeparam name="T">栈元素的类型</typeparam>
    public class BindableStack<T> : IEnumerable<T>, IEnumerable
    {
        private Stack<T> mStack = new Stack<T>();
        
        /// <summary>
        /// 当元素压入栈时触发的事件
        /// </summary>
        public event Action<T> OnItemPushed;
        
        /// <summary>
        /// 当元素从栈中弹出时触发的事件
        /// </summary>
        public event Action<T> OnItemPopped;
        
        /// <summary>
        /// 当栈被清空时触发的事件
        /// </summary>
        public event Action OnCleared;
        
        /// <summary>
        /// 当栈内容发生任何变化时触发的事件
        /// </summary>
        public event Action OnChanged;
        
        /// <summary>
        /// 获取栈中元素的数量
        /// </summary>
        public int Count => mStack.Count;
        
        /// <summary>
        /// 将元素压入栈顶
        /// </summary>
        /// <param name="item">要压入栈的元素</param>
        public void Push(T item)
        {
            mStack.Push(item);
            OnItemPushed?.Invoke(item);
            OnChanged?.Invoke();
        }
        
        /// <summary>
        /// 移除并返回位于栈顶的元素
        /// </summary>
        /// <returns>位于栈顶的元素</returns>
        public T Pop()
        {
            T item = mStack.Pop();
            OnItemPopped?.Invoke(item);
            OnChanged?.Invoke();
            return item;
        }
        
        /// <summary>
        /// 返回位于栈顶的元素但不将其移除
        /// </summary>
        /// <returns>位于栈顶的元素</returns>
        public T Peek()
        {
            return mStack.Peek();
        }
        
        /// <summary>
        /// 确定栈中是否包含特定元素
        /// </summary>
        /// <param name="item">要在栈中定位的元素</param>
        /// <returns>如果在栈中找到item，则为true；否则为false</returns>
        public bool Contains(T item)
        {
            return mStack.Contains(item);
        }
        
        /// <summary>
        /// 清空栈中的所有元素
        /// </summary>
        public void Clear()
        {
            mStack.Clear();
            OnCleared?.Invoke();
            OnChanged?.Invoke();
        }
        
        /// <summary>
        /// 将栈中的元素复制到一个新数组中
        /// </summary>
        /// <returns>包含栈中元素的数组</returns>
        public T[] ToArray()
        {
            return mStack.ToArray();
        }
        
        /// <summary>
        /// 获取一个枚举器，用于遍历栈
        /// </summary>
        /// <returns>可用于遍历栈的枚举器</returns>
        public IEnumerator<T> GetEnumerator()
        {
            return mStack.GetEnumerator();
        }
        
        /// <summary>
        /// 获取一个枚举器，用于遍历栈
        /// </summary>
        /// <returns>可用于遍历栈的枚举器</returns>
        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
        
        /// <summary>
        /// 返回栈中所有元素的字符串表示形式
        /// </summary>
        /// <returns>栈的字符串表示形式</returns>
        public override string ToString()
        {
            return $"BindableStack<{typeof(T).Name}> (Count: {Count})";
        }
    }
}